Skip to main content

07 IIR实验

Infinite Impulse Response 无限冲激响应

up的视频讲解: https://space.bilibili.com/1455872040

up视频里的实验代码: 👣IIR实验代码

1 🌼 IIR 简介​

1 把上次的结果也用起来​

FIR用过去的输入做加权求和。这一节我们再加一个思路:把上次已经算好的输出也用进来。

最简单的一阶平滑就是:

y[n] = α × x[n] + (1−α) × y[n−1],0 < α ≤ 1

α越小,当前输入所占的比例越小,曲线更平滑,但响应也更慢。输入一个1之后再持续输入0,过去的输出还会一次次影响下一次输出,理论上会无限延续,因此叫无限冲激响应。

static float last_y = 0.0f;
float Smooth_Process(float x)
{
last_y += 0.1f * (x - last_y);
return last_y;
}

这个例子适合传感器平滑。α本身不是赫兹,改变采样率之后,滤波器对应的实际频率特性也会改变。常见的 α=1−exp(−2πfc/Fs)是依据模拟时间常数做的映射,不能在任意截止频率下都当成精确的数字−3 dB设计。

2 ➡️从一阶走到二阶​

更常用的二阶节写成:

y[n] = b0·x[n] + b1·x[n−1] + b2·x[n−2]
− a1·y[n−1] − a2·y[n−2]

这里已经把a0归一化为1。一个二阶节也叫Biquad,多个二阶节串联可以实现更高阶滤波器。不要把高阶多项式直接硬塞进一条很长的差分方程,系数量化和舍入误差会更难控制。

反馈同时带来了稳定性问题。对本节这样的因果有理滤波器,检查极点是否严格落在单位圆内;若反馈符号写反,即使系数原本是正确的,程序也可能迅速发散。

3 🔧设计二阶Butterworth低通​

继续使用8 kHz采样、500 Hz与2000 Hz的混合信号,设计1000 Hz截止的二阶Butterworth低通。

from scipy import signal
sos = signal.butter(2, 1000, fs=8000, output="sos")
print(sos) # 每行 b0, b1, b2, a0, a1, a2

Butterworth的截止频率是幅度下降到 1/√2 的位置,约−3.01 dB。SOS形式方便逐节处理,也适合进一步增加阶数。SciPy Butterworth设计说明

这组参数的CMSIS系数约为:

/* b0, b1, b2, -a1, -a2 */
{0.0976310729f, 0.1952621459f, 0.0976310729f,
0.9428090416f, -0.3333333333f}

为什么末尾两个数要取反?因为CMSIS DF1采用加反馈项的约定,而上面SciPy的a系数对应减反馈项。必须先除以a0,再转换符号。Arm DF1符号约定

4 💻实验代码​

使用FIR实验中的合成输入生成方式。下载系数头文件,并把下面代码放进已经接好CMSIS-DSP的工程。

#include "arm_math.h"
#include "filter_coeffs.h"
#define BLOCK_SIZE 256U

static arm_biquad_casd_df1_inst_f32 iir;
/* 每节保存 x[n-1], x[n-2], y[n-1], y[n-2] */
static float32_t iir_state[4U * IIR_STAGES];

void IIR_Experiment_Init(void)
{
arm_biquad_cascade_df1_init_f32(&iir, IIR_STAGES, iir_coeffs, iir_state);
}

void IIR_Experiment_Block(const float32_t *input, float32_t *output)
{
arm_biquad_cascade_df1_f32(&iir, input, output, BLOCK_SIZE);
}

初始化只执行一次,每来一个连续的256点数据块,调用一次处理函数。input/output各分配256个float,历史状态保存在iir_state中。不要在中断里做整帧滤波或串口打印。

为了理解库函数内部在做什么,可以先独立运行下面的单点实现,再与CMSIS输出逐点比较。它使用本节已经转换成加反馈形式的系数:

static float x1, x2, y1, y2;
float Biquad_Process(float x)
{
float y = iir_coeffs[0] * x + iir_coeffs[1] * x1
+ iir_coeffs[2] * x2 + iir_coeffs[3] * y1
+ iir_coeffs[4] * y2;
x2 = x1; x1 = x;
y2 = y1; y1 = y;
return y;
}

5 ✅验证结果与稳定性​

先测试冲激和阶跃,再测试正弦,最后才接真实ADC。

测试理论/数值参考
零状态、全零输入输出保持0
单位冲激逐渐衰减到接近0,不持续增长
单位阶跃最终接近1;过渡过程可能有过冲
500 Hz单音增益约0.974,即约−0.23 dB
1000 Hz单音增益约0.707,即约−3.01 dB
2000 Hz单音增益约0.169,即约−15.44 dB

这些是8 kHz采样、上述二阶系数的稳态参考,不是实板测量结果。可以运行验证脚本复算。对混合信号,2000 Hz输入幅度是0.5,所以该分量输出幅度约为0.0845,不能直接和增益0.169混为一谈。

本例极点模均约为0.57735,小于1;因此精确系数下的系统稳定。输入有界而输出持续变大时,应查实现与数据,不应把“无限冲激响应”理解成幅值无限增大。

若输出突然变成很大的数或NaN,按顺序检查:a0归一化、反馈符号、输入是否越界、状态数组是否被其他变量覆盖、是否把定点数据当浮点使用。换成Q15/Q31之后还要重新检查动态范围、缩放和溢出,不能只替换函数名。

6 FIR与IIR怎么选​

比较点本节之前的31抽头FIR本节二阶IIR
历史信息过去输入过去输入和输出
单个输出的乘法数31次5次
相位对称系数提供线性相位一般非线性相位
稳定性有限系数的理想FIR为BIBO稳定需要检查极点、量化与实现
滤波强度由实际频响决定由实际频响决定

这两个例子阶数和截止定义不同,不能据此宣布某一种“效果更好”。正确的比较要先统一通带、阻带、允许延迟和算力预算,再设计各自的滤波器。

7 🔌接回课程硬件​

真实连续采样使用循环DMA方案,固定采样率,跨块保留状态。单帧停采演示则每帧作为独立实验,重新初始化并观察暂态,不能假装没有停采。

若把结果送回外置MCP4725,要先恢复直流偏置、限制到0~4095,并检查IIC更新带宽。本课程的外置IIC DAC不能按片内DAC的DMA触发例程使用。

最后做一个综合实验:同一段数据分别经过FIR和IIR,再用FFT观察500 Hz和2000 Hz分量,记录幅度、延迟与处理耗时。这样我们就把“采集—搬运—分析—处理”这条线完整连起来了。